/*
 * Copyright © 2009 Joonas Pihlaja
 * Copyright © 2009 Chris Wilson
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that copyright
 * notice and this permission notice appear in supporting documentation, and
 * that the name of the copyright holders not be used in advertising or
 * publicity pertaining to distribution of the software without specific,
 * written prior permission.  The copyright holders make no representations
 * about the suitability of this software for any purpose.  It is provided "as
 * is" without express or implied warranty.
 *
 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
 * OF THIS SOFTWARE.
 */
/* Usage:
 *   ./make-cairo-boilerplate-constructors [sources.c...] >cairo-boilerplate-constructors.c
 *
 * Parses invocations of the CAIRO_BOILERPLATE macro from the source files
 * given on the command line, gathers names of targets, and outputs a C
 * file with one function _cairo_boilerplate_register_targets() which
 * calls the functions _register_<target>() in reverse order.
 */

/* Keep this file ANSI compliant without any special needs. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define NAME "make-cairo-boilerplate-constructors.c"

static struct name {
    struct name *next;
    char         name[1];
} *head;

static void *
xmalloc (size_t n)
{
    void *bytes = malloc(n);
    if (!bytes) {
        fprintf (stderr, "Out of memory\n");
        exit(2);
    }
    return bytes;
}

static void
add_name (const char *name)
{
    struct name *node;
    int len;

    len = strlen (name);
    node = xmalloc (sizeof (struct name) + len);
    memcpy (node->name, name, len + 1);

    node->next = head;
    head = node;
}

static int
scan_file (const char   *filename,
           FILE         *fp)
{
    int line_num = 0;
    char linebuf[1024];
    int fail = 0;

    while (fgets (linebuf, sizeof (linebuf)-1, fp)) {
        char *macro;
        char *name;
        size_t length;

        line_num++;
        linebuf[sizeof (linebuf)-1] = 0;

        macro = strstr (linebuf, "CAIRO_BOILERPLATE");
        if (!macro)
            continue;
        macro += strlen ("CAIRO_BOILERPLATE");

        length = strspn (macro, " (");
        if (length == 0)
            continue;
        name = macro + length;

        length = strspn (name,
                         "_"
                         "abcdefghijklmnopqrstuvwxyz"
                         "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                         "0123456789");
        name[length] = 0;

        if (length == 0) {
            fprintf (stderr, "%s:%d: CAIRO_BOILERPLATE invocation "
                     "can't be parsed by " NAME"\n",
                     filename, line_num);
            fail = 1;
            continue;
        }

        add_name (name);
    }

    return fail;
}

int
main (int argc, char **argv)
{
    int i;
    int fail = 0;
    struct name *node;

    for (i=1; i<argc; i++) {
        FILE *fp = fopen (argv[i], "r");
        if (fp) {
            fail |= scan_file (argv[i], fp);
            fclose (fp);
        }
    }
    if (fail)
        exit(1);

    puts ("/* WARNING: Autogenerated file - see " NAME "! */");
    puts ("");
    puts ("#include \"cairo-boilerplate-private.h\"");
    puts ("");

    for (node = head; node; node = node->next) {
        printf ("extern void _register_%s (void);\n",
               node->name);
    }
    puts("");

    puts ("void _cairo_boilerplate_register_all (void);");
    puts("");

    puts ("void");
    puts ("_cairo_boilerplate_register_all (void)");
    puts ("{");
    for (node = head; node; node = node->next) {
        printf ("    _register_%s ();\n", node->name);
    }
    puts ("}");

    return 0;
}
